package sonnensystem;

/**
 *
 * @author Sebastian Bauer
 */
public class Planet extends Himmelskoerper {

    private static int ANZ_STUETZSTELLEN_POS = 50;
    
    /**
     * Rotationszeit um Faktor x verlangsamen, z.B. würde bei 60 ein Tag in
     * 1 Minute ablaufen.
     */
    private static int VERLANGSAMUNG_ROTATION = 1;
    
    /**
     * Umlauf um Faktor x verlangsamen, z.B. würde bei 60 ein Jahr (365 Tage) in
     * 1 Minute ablaufen.
     */
    private static int VERLANGSAMUNG_UMLAUF = 10;
    private double rotation; // Eigendrehung
    private double umlaufzeit; // Umlauf um die Sonne
    private String texturpfad;
    private double abstand; // Abstand zur Sonne

    public Planet() {
        super();
    }

    public String getTexturpfad() {
        return texturpfad;
    }

    public void setTexturpfad(String texturpfad) {
        this.texturpfad = texturpfad;
    }

    public double getUmlaufzeit() {
        return umlaufzeit;
    }

    public void setUmlaufzeit(double umlaufzeit) {
        this.umlaufzeit = umlaufzeit;
    }

    public double getRotation() {
        return rotation;
    }

    public void setRotation(double rotation) {
        this.rotation = rotation;
    }

    public double getAbstand() {
        return abstand;
    }

    public void setAbstand(double abstand) {
        this.abstand = abstand;
    }

    private String bauePlanetenDef() {

        String appearanceInhalt;

        if (this.texturpfad != null) {
            appearanceInhalt = "\t\t\t\t texture ImageTexture { url [\"" + this.texturpfad + "\"] } \n";
        } else if (this.farbe != null) {
            appearanceInhalt = "\t\t\t\t material Material {\n"
                    + "\t\t\t\t\t diffuseColor " + this.farbeNachVRML() + "\n"
                    + "\t\t\t\t }\n";

        } else {
            appearanceInhalt = "\n";
        }

        return "DEF " + this.name + " Transform {\n"
                + "\t translation 0 0 " + this.abstand + "\n"
                + "\t children [\n"
                + "\t\t Shape {\n"
                + "\t\t\t geometry Sphere {\n"
                + "\t\t\t\t radius " + this.radius + "\n"
                + "\t\t\t }\n"
                + "\t\t\t appearance Appearance {\n"
                + appearanceInhalt
                + "\t\t\t }\n"
                + "\t\t}\n"
                + "\t ]\n"
                + "}\n";
    }

    private String baueOrientationInterpolator() {

        return "DEF OI" + this.name + " OrientationInterpolator {\n"
                + "\t key [0.0 0.25 0.5 0.75 1.0]\n"
                + "\t keyValue [\n"
                + "\t\t0 1 0 0.00, #Ausgangsposition\n"
                + "\t\t0 1 0 1.57, #Vierteldrehung\n"
                + "\t\t0 1 0 3.14, #halbe Drehung\n"
                + "\t\t0 1 0 4.71, #Dreivierteldrehung\n"
                + "\t\t0 1 0 6.28, #komplett gedreht\n"
                + "\t ]\n"
                + "}\n";
    }

    private String baueOrientationTimer() {
        return "DEF " + this.name + "OrientationTimer" + " TimeSensor {\n"
                + "\tcycleInterval " + this.rotation * VERLANGSAMUNG_ROTATION + "\n"
                + "\tloop TRUE\n"
                + "}\n";
    }

    private String bauePositionInterpolator() {
        return "DEF PI" + this.name + " PositionInterpolator {\n"
                + "\t" + "key ["
                + berechneKeyPosInterpolator() + "]\n"
                + "\t keyValue [\n"
                + this.berechneKeyValuePosInterpolator()
                + "\t]\n"
                + "}\n";

    }

    private String bauePositionTimer() {
        return "DEF " + this.name + "PositionTimer" + " TimeSensor {\n"
                + "\tcycleInterval " +  this.umlaufzeit / 365.0 * VERLANGSAMUNG_UMLAUF + "\n"
                + "\tloop TRUE\n"
                + "}\n";
    }

    private String baueRoute() {
        return "ROUTE " + this.name + "OrientationTimer" + ".fraction_changed TO "
                + "OI" + this.name + ".set_fraction\n"
                + "ROUTE OI" + this.name + ".value_changed TO "
                + this.name + ".set_rotation\n"
                + "ROUTE " + this.name + "PositionTimer" + ".fraction_changed TO "
                + "PI" + this.name + ".set_fraction\n"
                + "ROUTE PI" + this.name + ".value_changed TO "
                + this.name + ".set_translation\n";
    }

    private String berechneKeyPosInterpolator() {
        StringBuilder key = new StringBuilder();

        for (int i = 0; i <= ANZ_STUETZSTELLEN_POS; i++) {
            key.append((double) i / ANZ_STUETZSTELLEN_POS);
            key.append(" ");
        }
        return key.toString();
    }

    private String berechneKeyValuePosInterpolator() {
        StringBuilder keyvalue = new StringBuilder();

        // aktuelle Position, wird in jedem Schritt aktualisiert,
        // um Position nach einem, zwei, drei, ... Tagen zu berechnen
        // initialisiert mit "Startposition" des Planeten auf der x-Achse
        double[] currentPos = {this.abstand, 0, 0};

        // Startposition wird als erstes KeyValue gesetzt
        this.appendKeyValues(keyvalue, currentPos);

        double[] tmp = new double[3];

        // Rotationsmatrix, die Bewegung an einem Tag berechnet
        double[][] rotMatrix = {{Math.cos(2 * Math.PI / ANZ_STUETZSTELLEN_POS), 0, -Math.sin(2 * Math.PI / ANZ_STUETZSTELLEN_POS)},
            {0, 1, 0},
            {Math.sin(2 * Math.PI / ANZ_STUETZSTELLEN_POS), 0, Math.cos(2 * Math.PI / ANZ_STUETZSTELLEN_POS)}
        };

        // 1 Tag entspricht 1/365 Timer-Sekunde
        // jeder Durchlauf aktualisiert die Position des Planeten tagesgenau
        // Zählerbedingung stellt sicher, dass genug Werte erzeugt werden
        // bei 5 Sekunden also z. B. 1825 Werte
        for (int i = 0; i < ANZ_STUETZSTELLEN_POS; i++) {

            tmp[0] = 0.0;
            tmp[1] = 0.0;
            tmp[2] = 0.0;
            // berechne neue Position durch Matrix-Vektor-Multiplikation
            for (int j = 0; j < 3; j++) {
                for (int k = 0; k < 3; k++) {
                    tmp[j] += rotMatrix[j][k] * currentPos[k];
                }
            }

            currentPos[0] = tmp[0];
            currentPos[1] = tmp[1];
            currentPos[2] = tmp[2];
            // füge neu berechnete Position zu den KeyValues hinzu
            this.appendKeyValues(keyvalue, currentPos);

        }
        return keyvalue.toString();
    }

    // Hilfsmethode, die neue KeyValues anhängt
    private void appendKeyValues(StringBuilder builder, double[] pos) {
        builder.append("\t\t");
        builder.append(pos[0]);
        builder.append(" ");
        builder.append(pos[1]);
        builder.append(" ");
        builder.append(pos[2]);
        builder.append("\n");
    }

    @Override
    public String baueVRML() {
        return this.bauePlanetenDef() + "\n"
                + this.baueOrientationInterpolator() + "\n"
                + this.bauePositionInterpolator() + "\n"
                + this.baueOrientationTimer() + "\n"
                + this.bauePositionTimer() + "\n"
                + this.baueRoute() + "\n";
    }
}
